关于weex 您所在的位置:网站首页 weex 动画 封装 关于weex

关于weex

2023-06-28 22:50| 来源: 网络整理| 查看: 265

生命周期 module.exports = { data: {}, methods: {}, init: function () { console.log('在初始化内部变量,而且添加了事件功能后被触发'); }, created: function () { console.log('完成数据绑定以后,模板编译以前被触发'); }, ready: function () { console.log('模板已经编译而且生成了 Virtual DOM 以后被触发'); }, destroyed: function () { console.log('在页面被销毁时调用'); } }

init内通常用于初始化一些内部变量,绑定一些自定义事件,这时尚未数据绑定,没有建立vdom,因此不能经过this获取到data和methods,也不能获取vdom的节点css

created 完成了数据绑定 ,但还未开始编译模板,能够经过this获取data和methods,但不能获取vdom的节点html

ready表示渲染完成 ,从子组件往上触发前端

destroyed 组件销毁,好比页面跳转,从子组件开始往上触发node

工做原理

clipboard.png

clipboard.png

clipboard.png

Weex设计之初就考虑到在三端(iOS、安卓和H5)上可以获得展示。在最上面的DSL,阿里通常称之为Weex文件(.we),经过Transformer转换成js-bundle,再部署到服务器,这样服务端就完成了。在客户端,第一层是JS-Framework,最后到RenderRengine。webpack

clipboard.png

输入是Virtual DOM输出是native或者H5 view,还原成内存中的树型数据结构,再建立view,把事件绑定在view上,把view基本属性设上去。Weex Render会分三个线程,不一样的线程负责不一样的事情,让JS线程优先保障流畅性。git

工做模式

Weex的三种工做模式。github

全页模式web

目前支持单页使用或整个App使用Weex开发(还不完善,须要开发Router和生命周期管理),这是主推的模式,能够类比RN。npm

Native Component模式数组

把Weex看成一个iOS/Android组件来使用,类比ImageView。这类需求遍及手淘主链路,如首页、主搜结果、交易组件化等,这类Native页面主体已经很稳定,可是局部动态化需求旺盛致使频繁发版,解决这类问题也是Weex的重点。

H5 Component模式

在H5种使用Weex,类比WVC。一些较复杂或特殊的H5页面短时间内没法彻底转为Weex全页模式(或RN),好比互动类页面、一些复杂频道页等。这个痛点的解决办法是:在现有的H5页面上作微调,引入Native解决长列表内存暴增、滚动不流畅、动画/手势体验差等问题。

另外,WVC将会融入到Weex中,成为Weex的H5 Components模式。

各类文档 官方

官网 http://alibaba.github.io/weex/官方英文 http://alibaba.github.io/weex...官方示例 http://alibaba.github.io/weex...Weex Playground http://alibaba.github.io/weex...github https://github.com/alibaba/weex工程开发套件 https://github.com/weexteam/w...命令行工具 https://github.com/weexteam/w...调试工具 https://github.com/weexteam/w...

第三方

awesome-weex https://github.com/joggerplus...vczero https://github.com/vczero/wee...h5weex开发相关文章 https://github.com/h5weex/h5w...weex中文文档 http://doc.weexstore.com/177086

社区讨论组

gitter https://gitter.im/weexteam/cnweex-help http://weex.help/weex.store http://weexstore.com/

demo

weex-demo-dusan 实现splash,guide,home页面,交互主要是点击,左右,上下滑动。https://github.com/duqian2919...

hello-weex包括一个Weex App,和本身扩展的WeexiOSKit。hello-weex https://github.com/coderyi/he...

toolbox-weex 一个小型的项目,帮助新手体会一下如何作出能够看、能够用的原生界面。https://github.com/hugojing/t...

模块化

weex不少功能都进行了模块化的封装,内置模块引用须要添加@weex-module前缀,使用require('@weex-module/name')能够进行引用。

现有模块包括:

dom

steam

modal

animation

webview

navigator

文章

阿里巴巴开源前端框架--Weex实践http://blog.csdn.net/zhangcan...

Weex详解:灵活的移动端高性能动态化方案 http://www.imooc.com/article/...

给正在学习Vuejs同窗的几个小Tipshttp://www.imooc.com/article/...

阿里无线11.11 : Weex——关于移动端动态性的思考、实现和将来 http://www.infoq.com/cn/artic...

细节 文件目录结构

Weex默认的文件结构是要求全部相关的we文件都在同一级目录下,以便能准确的找到依赖的组件,例如:

bar.we bar foo.we

当须要提取一些公共组件,这些公共组件通常存放在一个公共目录下(自建的目录或经过npm安装到node_modules目录),而这样的文件结构,也每每出如今一些完整的项目工程中,当经过上述的脚手架搭建好示例工程手,能够经过前端习惯的require方式来引用非相同目录下的we文件,例如:

components/bar.we bar foo.we require('./components/bar')

其背后的原理,其实是整个转换和打包过程借助了webpack以及weex-loader,使得其中的模块化定义遵循标准的The way of CommonJS。

引用标准JS文件

有了webpack的助力,在we文件中,也能轻松使用一个符合CommonJS规范的JS文件。例如,经过npm安装了业界No.1的工具库lodash:

foo.we {{foo + bar}} var _ = require('lodash') module.exports = { data: { foo: 'foo', bar: 'bar' }, created: function() { _.assign(this.data, {foo: 'the foo', bar: 'the bar'}) } }

##用上Tomorrow's css和ES2015现在前端的开发,通常离不开预处理器,好比postcss和babel。在默认的we文件中,即便有webpack的助力,这类预处理器也是对其无能为力的。为此,咱们须要拆分这个we文件,让它变成标准的html、css或js文件。

bar.we.html Hello {{name}} bar.we.css .hello { font-size: 40px; color: #333; } bar.we.js module.exports = { template: require('./foo.we.html'), style: require('./foo.we.css'), data: { name: 'Weex' } }

而且,须要在webpack.config.js中加入几个能解析这些特殊文件的loader:

loaders: [ { test: /\.we\.js(\?[^?]+)?$/, loaders: ['weex?type=script'] }, { test: /\.we\.css(\?[^?]+)?$/, loaders: ['weex?type=style'] }, { test: /\.we\.html(\?[^?]+)?$/, loaders: ['weex?type=tpl'] } ]

以后,仍然使用require的方式来引用这个'we'文件:

foo.we require('./bar.we.js')

当分割了we文件后,你就能够分别对其中的css或js文件使用你想要的预处理器。

!!!不过须要特别提醒的是,目前weex-loader只支持module.exports={...}的模块输出方式,因此即便你在js文件中用了ES6的import,但请勿使用export来导出模块

调用native提供的模块方法

Weex的代码自己是运行在js的runtime下的,因此为了和native进行通信,就须要借由hybrid的方式。其中,对于native提供的一系列模块方法,就须要用一种特殊,但直观的方式来调用。

本来,Weex中集成了一些预约义的API,例如this.$sendMtop。但这些预约义API的维护成本太高,所以在最新甚至之后的Weex版本中,会渐渐废弃这类预约义的API,而改用更加通用的方式:

var stream = require('@weex-module/stream') module.exports = { ready: function() { if (stream && stream.sendMtop) { stream.sendMtop(params, callback) } else { console.error('stream.sendMtop is invalid') } } }

这里又再次请出了万能的 require ,不过和普通的 require 不一样的是,须要指定特定的 @weex-module 前缀方能正确使用

慎用或不用异步函数

为了解释异步函数的在Weex中的危害,首先要理解在Weex中产生的两类task。

一类,是由Weex控制的js和native交互时产生的task(如下简称Weex的task),好比一系列异步调用native模块方法,或者点击事件等。

一类,是系统原生的task(如下简称原生的task),好比·setTimeout,Promise·。

在Weex的task中更新数据时,Weex能够自动更新View。而在原生的task中,由于Weex丧失了控制权,因此没法作到自动更新。这就致使,在原生的task中产生的diff,会滞留直到下一次Weex的task才会被触发更新View。从表面上看,就是在这些原生的task中改变数据后,并无及时反应到View上。为了,避免这个问题的产生,目前来讲并不推荐使用Promise。而对于setTimeout来讲,可使用native提供的timer.setTimeout的模块方法。

生命周期的一二三

Weex当前版本设计了组件的生命周期,如下的一张图能够比较直观的告诉你们在整个生命周期里都作了些什么事情:

clipboard.png

那么在这些生命周期的Hook里,能够作哪些事情呢:

在init中能够进行数据请求,好比mtop。但这个时候上下文中尚未data对象,同时也不建议在以后的任何阶段改变data的数据结构。在created中,能够对data进行操做了,且此时更新数据不会产生多余的diff,但切忌也不能更改data的数据结构。另外,能够经过this.$on来监听子组件的dispatch。在ready中,此时子组件已经ready,能够获取子组件的Vm对象了。而此时,若是更新数据,会产生多余的diff。特别提醒:这三个阶段,都是不容许更改data的数据结构的。

[Bug]设置样式的默认值

来看一个经过改变class来改变样式的例子:

Hello Weex .normal { font-size: 40px; } .hightlight { font-size: 40px; color: red; } module.exports = { data: { className: 'normal' }, methods: { toggle: function() { if (this.className === 'normal') { this.className = 'hightlight' } else { this.className = 'normal' } } } }

上述例子,经过点击来切换样式名。可是你会惊奇的发现,在最初一次切换以后,字体的颜色就一直是红色的了。

这实际上是目前Weex一个bug,讨论如何修复的issue在这里#397。缘由就是,在Weex中样式表样式的切换,并不会清除原来的样式。例如,当前样式是highlight,其中字体颜色是red,在切换到normal时,由于没有指定字体颜色,结果原来的red颜色就被保留了下来而并无清除掉。因此,在上面的例子中为了避开这个bug,须要显示的设置字体颜色:

.normal { font-size: 40px; color: black; }

另外,对于最佳实践来讲,能够经过组合class名称的方式,把须要切换的样式提取出来:

Hello Weex .common { font-size: 40px; } .normal { color: black; } .hightlight { color: red; } 元素上的属性定义

Weex拥有一套相似前端开发习惯的DSL,HTML和CSS部分也都会遵循W3C的标准。其中元素上的属性定义,对于非前端同窗来讲会有不少误区,这里务必要说明下。

属性名必须所有小写,可使用链接符-。属性值,尽可能保证是原始类型,即number/string/boolean/undefined/null。对象类型的值通常用于大数据量的数据绑定。一些HTML文章里会推荐在属性上用data-xxx的方式,这里并不须要特地加data前缀,由于Weex的js中并无dataset的API可供调用。

搞定子组件的数据绑定

趁热打铁,来讲下子组件的数据绑定。由于数据绑定也是经过属性来定义的,因此首先要遵循上一段所说的规则。

绑定数据经过属性来定义,不只须要在使用的元素上指定属性并绑定父组件中的数据,也要在子组件的data中指定对应的键,而且元素上的属性和子组件中的键名的对应规则是:若是属性中有链接符,则键名为去掉链接符后的驼峰写法,不然所有以小写命名。若是仅仅须要给子组件传递数据,而其中的数据结构对父组件是透明的,那么建议直接使用一个属性来映射;若是,属性是子组件的一些功能(且数量小于等于5个),则能够独立开来(基本上和API行为的设计原则差很少),例如:

sub1 module.exports = { data: { aMtopData: {} } } sub2 module.exports = { data: { option1: '', option2: '' } } module.exports = { data: { options: { op1: 'op1', op2: 'op2' }, mtopdata: {} }, created: function() { var self = this this.$sendMtop({...}, function(r) { self.mtopdata = r.data }) } } 遍历长列表

在咱们各种大型运营活动的页面中,你们对楼层/坑位这些词应该不陌生。而这些名词的界面,基本都要靠循环列表来完成。而循环列表的性能又是整个运营页面的关键。因此在遍历这样的列表或者数组的时候,就须要一些技巧。

一般来讲,由于存在楼层的概念,而楼层里又是多个坑位,坑位又常常是双列宝贝,眼瞅着这得用个三重循环才能搞定。不过实际上,双列宝贝能够优化成不使用循环的结构。固然了,前端的童靴们必定要对着大家的服务端童靴保持坚决立场,要求得到清晰且正确的数据结构,确保前端不须要对数据结构作二次处理。

一般的数据结构和对应的模板通常是这样的:

{{name}} {{items.list[0].name}} {{items.list[1].name}} .tabheader { flex-direction: row; } .items { flex-direction: row; } module.exports = { data: { floors: [ { floorId: 1, name: 'f1', items:[ {lineId: 1, list: [{itemId:1, name: 'i1'}, {itemId:2, name: 'i2'}]}, {lineId: 2, list: [{itemId:3, name: 'i3'}, {itemId:4, name: 'i4'}]} ] } ] }, computed: { headers: function() { return this.floors.map(function(v) { return {name: v.name} }) } }, methods: { update: function() { this.floors[0].items.push( {lineId: 3, list: [{itemId:5, name: 'i5'}, {itemId:6, name: 'i6'}]}, {lineId: 4, list: [{itemId:7, name: 'i7'}, {itemId:8, name: 'i8'}]} ) this.floors.push({ floorId: 2, name: 'f2', items: [ {lineId: 5, list: [{itemId:9, name: 'i9'}, {itemId:10, name: 'i10'}]}, {lineId: 6, list: [{itemId:11, name: 'i11'}, {itemId:12, name: 'i12'}]} ] }) } } }

其中比较常见的数据结构问题,好比items只是一个一维数组。若是能在服务端就处理好items的多维数组问题,那么前端的效率会高不少。

再仔细剖析其中的模板:

... computed: { headers: function() { return this.floors.map(function(v) { return {name: v.name} }) } }

这里绑定了一个computed特性的数据。当某类数据不太适合展现的时候,推荐能够用computed的方式来达到数据预处理的目的,而不是在created中吭哧吭哧的算一份新的数据结构出来。

... ...

在这三个repeat中都用了track-by。它的特色是,能够记录数组中某个项的一个主键,并在以后的更新中复用这个特定的项,而不是重构整个数组。例如

楼层1增长两行坑位 this.floors[0].items.push( {lineId: 3, list: [{itemId:5, name: 'i5'}, {itemId:6, name: 'i6'}]}, {lineId: 4, list: [{itemId:7, name: 'i7'}, {itemId:8, name: 'i8'}]} ) 增长一个楼层 this.floors.push({ floorId: 2, name: 'f2', items: [ {lineId: 5, list: [{itemId:9, name: 'i9'}, {itemId:10, name: 'i10'}]}, {lineId: 6, list: [{itemId:11, name: 'i11'}, {itemId:12, name: 'i12'}]} ] })

若是没有设置track-by,那么Weex会重构整个数组,致使元素被删除后又从新添加。而添加了id做为track-by的主键后,id相同的元素会经过移动的方式来优化操做。

优化长列表

长列表相信你们都作过。Weex中,对列表的优化已经很是接近原生系统的列表了,这个要归功于咱们的Native团队。但即便有了性能不错的列表,对于首屏的渲染仍是有追求的。

在Weex中,要作无尽列表其实很是简单,由于在list和scroll的元素上,已经实现了onloadmore事件,这个事件会在滚动触底(或者离底部必定的距离)时触发,因此这样看起来,作无尽列表变得很是容易。不过,这样的无尽列表体验绝对算不上极致。这个时候,能够借助loading这个组件,并配合onloading事件,来展示更加出色的无尽列表。

{{v.name}} {{loadingText}} module.exports = { data: { index: 0, size: 50, count: 10, loadingText: '加载更多...', items: [] }, created: function() { this.addPage() }, methods: { addPage: function() { for (var i = 0; i < this.size; i++) { var id = this.index * this.size + i this.items.push({id: id, name: 'item-' + id}) } this.index++ }, loadingHandler: function() { if (this.index === this.count) { this.loadingText = '没有更多了' } else { this.addPage() } } } } 设计优秀的Weex组件

在Weex原生功能愈来愈丰富的前提下,开发者能够设计出各种符合业务需求的UI组件,这些UI组件基本能够遵循标准的模块化开发,已达到复用和高度定制的目的。

用脚手架来初始化Weex组件的仓库再合适不过了。Weex是一种数据驱动的设计框架,组件并非经过API来暴露行为,而是经过数据绑定来给组件设置行为。组件的通讯,能够经过$dispath/$broadcast来完成,不过这得付出一点点性能的代价。而在父组件拿到直接子组件的对象后,其实能够经过$on/$emit来减小性能的开销。若是组件须要高度定制UI,能够考虑使用content/slot标签,具体能够参考下wxc-marquee。借由脚手架初始化Weex组件工程,能够轻松发布到npm/tnpm,而且开发者在经过npm install安装后,能够轻松的以require方式来引入这些组件。

调试代码(查看日志)

Weex将来会接入Chrome Dev-tools,甚至Debugger for IDE,这些均可以小小期待下的。而当下能够经过输出日志的原始方式来调试。

在最开始的利器一章中,我已经让你们安装了weex-toolkit,并拥有了weex命令。那么如今要用它来开启调试的大门:

weex --debugger或者在脚手架工程中运行:

npm run debugger这个时候会输出一段本地的ip地址,在浏览器里输入这个地址,会展现一个二维码。用手淘debug包或Playground扫码以后,就开启了输出日志模式。

clipboard.png

在这个界面中,你能够经过选择设备的日志级别,以及展现的输出级别来找到你想要的日志。同时在代码中,能够经过console.log/debug/info/warn/debug来输出相应级别的日志。

在日志debug级别中,以[js framework]开头的,即是js-framework的解析操做。在日志verbose级别中,以Calling JS和Calling Native开头的,就是js和native互相通讯的操做。

页面间通讯

页面跳转是经过指定下一个页面的url,而后经过openurl或者push的方式来跳转

获取url的方式能够经过下面这段JS代码

function getAppBaseUrl(self) { var dir ='examples' var url = self.$getConfig().bundleUrl; var bundleUrl = url; bundleUrl = new String(bundleUrl); var nativeBase; var isAndroidAssets = bundleUrl.indexOf('file://assets/') >= 0; var isiOSAssets = bundleUrl.indexOf('file:///') >= 0; if (isAndroidAssets) { nativeBase = 'file://assets/'; } else if (isiOSAssets) { nativeBase = bundleUrl.substring(0, bundleUrl.lastIndexOf('/') + 1); } else { var host = 'localhost:12580'; var matches = /\/\/([^\/]+?)\//.exec(self.$getConfig().bundleUrl); if (matches && matches.length >= 2) { host = matches[1]; } nativeBase = 'http://' + host + '/' + dir + '/build/'; } var h5Base = './index.html?page=./' + dir + '/build/'; //Native端 var base = nativeBase; //H5端 if (typeof window === 'object') { base = h5Base; } return base }

第六篇 导航、页面跳转、stream、webview

页面通讯有两种方式

1.经过 url 参数传递。

/** * 获取URL参数 */ getUrlParam: function (key) { var t = this.$getConfig().bundleUrl; var reg = new RegExp('[?|&]' + key + '=([^&]+)'); var match = t.match(reg); return match && match[1]; }

2.经过 localStorage 数据存储。

若是是组件间通讯不是页面通讯,则参考:组件之间通讯 - (Communicate Between Components)

weex cheatsheet

https://github.com/alibaba/we...



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有